﻿using System;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
using Av.Utils;
using System.Text;
using System.Collections.Generic;
using System.ComponentModel;

namespace SymcmsPackage
{
    public partial class Form1 : Form
    {

        /// <summary>
        /// Systray icon
        /// </summary>
        protected NotifyIcon m_icon = null;

        #region " Get/set text methods (invoked) "

        delegate void SetTextDelegate(string name, string value);
        delegate string GetTextDelegate(string name);

        /// <summary>
        /// Returns text of control.
        /// </summary>
        /// <param name="name">Name of control</param>
        /// <returns>Text of control. Returns "checked"/"" or "" for CheckBox</returns>
        private string GetText(string name)
        {
            string s = "";

            try
            {
                if(this.InvokeRequired)
                {
                    this.Invoke(new GetTextDelegate(GetText), name);
                } else
                {
                    Control[] ar = this.Controls.Find(name, false);
                    if((ar != null) && (ar.Length > 0))
                    {
                        if(ar[0].GetType() == typeof(CheckBox))
                        {
                            s = ((CheckBox)ar[0]).Checked ? "checked" : "";
                        } else
                        {
                            s = ar[0].Text;
                        }
                    }
                }
            } catch(Exception ex)
            {
            }

            return s;
        }

        /// <summary>
        /// Thread safe AddText
        /// </summary>
        /// <param name="name">Name of control to add text</param>
        /// <param name="value">Text to add</param>
        private void AddText(string name, string value)
        {
            try
            {
                if(this.InvokeRequired)
                {
                    this.Invoke(new SetTextDelegate(AddText), name, value);
                } else
                {
                    Control[] ar = this.Controls.Find(name, false);
                    if((ar != null) && (ar.Length > 0))
                    {
                        ar[0].Text += value;
                        if(ar[0].GetType() == typeof(TextBox))
                        {
                            ((TextBox)ar[0]).SelectionStart = Int32.MaxValue;
                        }
                    }
                }
            } catch(Exception)
            {
            }
        }

        /// <summary>
        /// Thread safe SetText
        /// </summary>
        /// <param name="name">Name of control to set text</param>
        /// <param name="value">Text to set</param>
        private void SetText(string name, string value)
        {
            try
            {
                if(this.InvokeRequired)
                {
                    this.Invoke(new SetTextDelegate(SetText), name, value);
                } else
                {
                    Control[] ar = this.Controls.Find(name, false);
                    if((ar != null) && (ar.Length > 0))
                    {
                        ar[0].Text = value;
                    }
                }

            } catch(Exception ex)
            {
            }
        }

        #endregion

        public Form1()
        {
            InitializeComponent();
        }

        private void OnFormClosed(object sender, FormClosedEventArgs e)
        {
            if(m_icon != null)
            {
                m_icon.Dispose();
            }
        }

        private void OnFormLoad(object sender, EventArgs e)
        {
            try
            {
                Log4cs.Log("Starting {0} (v{1})...", Settings.Name, Settings.Version);
                Debug("Starting {0}...", Settings.NameVersion);

                // In case we need only one instance of program
                EnsureSingleApplication();

                this.Text = Settings.NameVersion;

                // Creates icons using emebedded icon
                CreateFormIcons();

                // Create context menu for tray icon
                MenuItem[] arMenu = this.CreateMenuItems();
                if(arMenu != null)
                {
                    m_icon.ContextMenu = new ContextMenu(arMenu);
                }

                // Set to Mini
                lstSymcmsType.SelectedIndex = 0;

                DisplayAdditionalModules();
                txtSymcmsPath.Text = "D:\\temp\\symcms\\";

                this.SetText("lblLogs", string.Format("Logs (Check {0}\\Logs\\ directory)", Directory.GetCurrentDirectory()));
                
                // Hides application (form) if necessary
                //HideApplication();

            } catch(ApplicationException ex)
            {
                Debug(Importance.Error, "Application already run, check logs!");
                MessageBox.Show(ex.ToString(), string.Format("{0} (v{1})", Settings.Name, Settings.Version), MessageBoxButtons.OK, MessageBoxIcon.Error);
                this.Close();
            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error loading main form!");
                Log4cs.Log(Importance.Debug, ex.ToString());
                MessageBox.Show("Error loading application!", string.Format("{0} (v{1})", Settings.Name, Settings.Version), MessageBoxButtons.OK, MessageBoxIcon.Error);
                this.Close();
            }
        }

        /// <summary>
        /// Hides application (form) and from taskbar
        /// </summary>
        protected void HideApplication()
        {
            this.ShowInTaskbar = false;
            this.Visible = false;
        }

        /// <summary>
        /// Hides from Alt-TAB
        /// </summary>
        //protected override CreateParams CreateParams
        //{
        //    get
        //    {
        //        CreateParams cp = base.CreateParams;

        //        // Turns on WS_EX_TOOLWINDOW style bit to hide from Alt-TAB list
        //        cp.ExStyle |= 0x80;

        //        return cp;
        //    }
        //}

        /// <summary>
        /// Creates tray icon from embedded to project icon. Throws execptions
        /// </summary>
        private void CreateFormIcons()
        {
            // Create tray icon
            Assembly asm = Assembly.GetExecutingAssembly();
            FileInfo fi = new FileInfo(asm.GetName().Name);
            using(Stream s = asm.GetManifestResourceStream(string.Format("{0}.av1.ico", fi.Name)))
            {
                // Create icon to be used in Form and Tray
                Icon icon = new Icon(s);

                Icon = new Icon(icon, icon.Size);

                m_icon = new NotifyIcon();
                m_icon.Visible = true;
                m_icon.Icon = new Icon(icon, icon.Size);

                icon.Dispose();
            }
        }

        /// <summary>
        /// Ensures that application is the only. Throws ApplicationException if there is such application
        /// </summary>
        private void EnsureSingleApplication()
        {
            bool createdNew = false;
            Mutex mx = new Mutex(false, Settings.Name, out createdNew);
            Log4cs.Log(Importance.Debug, "Is mutex created: {0}", createdNew);

            // If application is already running
            if(createdNew == false)
            {
                throw new ApplicationException(Settings.Name + " application is already running!");
            }
        }


        #region " Debug to UI console "

        /// <summary>
        /// Outputs message to UI output console
        /// </summary>
        /// <param name="msg">Message to input, could be used like string.Format()</param>
        /// <param name="args"></param>
        protected void Debug(string msg, params object[] args)
        {
            Debug(Importance.Info, msg, args);
        }

        /// <summary>
        /// Outputs message to UI output console
        /// </summary>
        /// <param name="level">Level of imortance - Error, Info, etc...</param>
        /// <param name="msg">Message to input, could be used like string.Format()</param>
        /// <param name="args"></param>
        protected void Debug(Importance level, string msg, params object[] args)
        {
            StringBuilder sb = new StringBuilder();
            if(level != Importance.No)
            {
                sb.AppendFormat("[{0}] ", level.ToString().ToUpper());
            }

            sb.AppendFormat(msg, args);
            sb.AppendLine();
            this.AddText("txtOutput", sb.ToString());
            txtOutput.SelectionStart = int.MaxValue;
            txtOutput.ScrollToCaret();
        }

        #endregion


        #region " Context menu methods "

        /// <summary>
        /// Creates context menu for tray icon
        /// </summary>
        /// <returns></returns>
        private MenuItem[] CreateMenuItems()
        {
            MenuItem[] arMenu = null;

            try
            {
                // Got quantity of menus
                arMenu = new MenuItem[MyMenu.Size];
                
                //int[] arMenuIds = MyMenu.GetMenuItems();
                //arMenu = new MenuItem[arMenuIds.Length];

                for(int i = 0; i < MyMenu.Size; i++)
                {
                    arMenu[i] = new MenuItem(MyMenu.ToName(i), OnContextMenuClicked);
                }

                // By default status thread is stopped, so disable "Stop" command
                arMenu[MyMenu.Position.Stop].Enabled = false;

                // Format "Version"
                arMenu[MyMenu.Position.Version].Text = string.Format("{0} (v{1})", Settings.Name, Settings.Version);

            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error creating menu items!");
                Log4cs.Log(Importance.Debug, ex.ToString());
            }

            return arMenu;
        }

        /// <summary>
        /// Handles clicks on systray icon menu
        /// </summary>
        void OnContextMenuClicked(object sender, EventArgs e)
        {
            try
            {
                switch(((MenuItem)sender).Index)
                {
                    case MyMenu.Position.Start:
                        m_icon.ContextMenu.MenuItems[MyMenu.Position.Start].Enabled = false;
                        m_icon.ContextMenu.MenuItems[MyMenu.Position.Stop].Enabled = true;
                        OnStartStopClicked(null, null);
                        break;
                    case MyMenu.Position.Stop:
                        m_icon.ContextMenu.MenuItems[MyMenu.Position.Start].Enabled = true;
                        m_icon.ContextMenu.MenuItems[MyMenu.Position.Stop].Enabled = false;
                        OnStartStopClicked(null, null);
                        break;
                    case MyMenu.Position.Version:
                        MessageBox.Show("Simple symcms package by mr. Aleksej Vasinov", Settings.NameVersion, MessageBoxButtons.OK, MessageBoxIcon.Information);
                        break;
                    case MyMenu.Position.Reload:
                        ReloadSettings();
                        break;
                    default:
                        this.Close();
                        break;
                }

            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error handling context menu click!");
                Log4cs.Log(Importance.Debug, ex.ToString());
            }
        }


        /// <summary>
        /// Reloads setting. Stops thread if necessary
        /// </summary>
        private void ReloadSettings()
        {
            Log4cs.Log("Reloading settings...");
            Settings.Load();
        }

        /// <summary>
        /// Starts/stops application if needed :)
        /// </summary>
        private void OnStartStopClicked(object sender, object e)
        {
            Log4cs.Log("Start/stop is clicked...");
        }

        #endregion

        private void OnCreateProjectClicked(object sender, EventArgs e)
        {
            string projectType = "";
            try
            {
                projectType = lstSymcmsType.SelectedItem.ToString().ToLower();
                Debug("Creating symcms project by type {0}...", projectType);
                // Create and configure what we need to export
                SymcmsCreatorBase creator = SymcmsCreator.GetCreator(projectType);
                creator.InputDir = txtSymcmsPath.Text;
                creator.OutputDir = string.Format("{0}\\_packages\\{1}\\", creator.InputDir, projectType);
                creator.SymfonyInstallDir = txtSymfonyInstallDir.Text.Trim();

                // Run in background
                backgroundWorker.RunWorkerAsync(creator);
            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error creating symcms project {0}!", projectType);
                Log4cs.Log(Importance.Debug, ex.ToString());
                Debug(Importance.Error, "Error creating symcms project {0}!", projectType);
            }
        }

        /// <summary>
        /// Asyn-ly do symcms package creation
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                BackgroundWorker bw = (BackgroundWorker)sender;
                SymcmsCreatorBase creator = e.Argument as SymcmsCreatorBase;
                creator.Create(/*inputDir, outputDir*/);
                e.Result = string.Format("Export is done, check {0}", creator.OutputDir);
            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error in background worker!");
                Log4cs.Log(Importance.Debug, ex.ToString());
                throw ex;
            }
        }

        /// <summary>
        /// Outputs message on job done
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnBackgroundWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if(e.Error != null)
            {
                Debug("Error exporting symcms package!");
            } else
            {
                Debug(e.Result.ToString());
            }

        }

        private void DisplayAdditionalModules()
        {
            SymcmsModule[] modules = new SymcmsModule[]
            {
                new SymcmsModule(){ Name = "FAQ", ModuleName = "faq" }
                , new SymcmsModule(){ Name = "Remarks", ModuleName = "remarks" }
                , new SymcmsModule(){ Name = "File storage", ModuleName = "storage" }
                , new SymcmsModule(){ Name = "News letter", ModuleName = "newsletter" }
                , new SymcmsModule(){ Name = "Partners", ModuleName = "partner" }
                , new SymcmsModule(){ Name = "Sitemap", ModuleName = "sitemap" }
                , new SymcmsModule(){ Name = "Youtube integration", ModuleName = "video" }
            };
            checkedListAdditionalModules.Items.AddRange(modules);
        }

        private void OnTestButtonClicked(object sender, EventArgs e)
        {
            Debug("Starting background worker");
            backgroundWorker.RunWorkerAsync();
            //Common.CopyDirectory("D:\\temp\\symcms\\doc\\sql\\", "E:\\xxx\\");
            return;

            #region " Test libraries "
            string[] ar = new string[]{
                "BannerItem.php"
                , "BannerItemPeer.php"
                , "BannerStat.php"
                , "BannerStatPeer.php"
                , "Countries.php"
                , "CountriesPeer.php"
                , "Draft.php"
                , "DraftPeer.php"
                , "Files.php"
                , "FilesI18n.php"
                , "FilesI18nPeer.php"
                , "FilesPeer.php"
                , "Gallery.php"
                , "GalleryI18n.php"
                , "GalleryI18nPeer.php"
                , "GalleryPeer.php"
                , "GeoName.php"
                , "GeoNamePeer.php"
                , "Log.php"
                , "LogPeer.php"
                , "MailTask.php"
                , "MailTaskPeer.php"
                , "News.php"
                , "NewsI18n.php"
                , "NewsI18nPeer.php"
                , "Newsletter.php"
                , "NewsletterPeer.php"
                , "NewsPeer.php"
                , "Seo.php"
                , "SeoPeer.php"
                , "SymConfig.php"
                , "SymConfigPeer.php"
                , "TextModule.php"
                , "TextModuleI18n.php"
                , "TextModuleI18nPeer.php"
                , "TextModulePeer.php"
                , "Tree.php"
                , "TreeLng.php"
                , "TreeLngPeer.php"
                , "TreePeer.php"
                , "Uid.php"
                , "UidPeer.php"
                , "User.php"
                , "UserPeer.php"
                , "Video.php"
                , "VideoI18n.php"
                , "VideoI18nPeer.php"
                , "VideoPeer.php"
            };

            List<string> names = new List<string>();
            StringBuilder sbModel = new StringBuilder();
            StringBuilder sbOm = new StringBuilder();
            StringBuilder sbMap = new StringBuilder();
            string name = "";
            foreach(string f in ar)
            {
                name = f.EndsWith(".php") ? f.Substring(0, f.Length - 4) : f;
                name = name.EndsWith("Peer") ? name.Substring(0, name.Length - 4) : name;
                //name = name.EndsWith("I18n") ? name.Substring(0, name.Length - 4) : name;
                if(!names.Contains(name))
                {
                    names.Add(name);
                    sbModel.AppendFormat("{0}.php", name).AppendLine();
                    sbModel.AppendFormat("{0}Peer.php", name).AppendLine();

                    sbOm.AppendFormat("Base{0}.php", name).AppendLine();
                    sbOm.AppendFormat("Base{0}Peer.php", name).AppendLine();

                    sbMap.AppendFormat("{0}MapBuilder.php", name).AppendLine();

                    //names.Add("sf10\\lib\\model\\" + name + ".php");
                    //names.Add("sf10\\lib\\model\\" + name + "Peer.php");
                    //names.Add("sf10\\lib\\model\\om\\Base" + name + ".php");
                    //names.Add("sf10\\lib\\model\\om\\Base" + name + "Peer.php");
                    //names.Add("sf10\\lib\\model\\map\\" + name + "MapBuilder.php");
                }
            }

            Debug("Model:{0}{1}", Environment.NewLine, sbModel.ToString());
            Debug("Om:{0}{1}", Environment.NewLine, sbOm.ToString());
            Debug("Map:{0}{1}", Environment.NewLine, sbMap.ToString());
            //foreach(string f in names)
            //{
            //    Debug(Importance.No, f);
            //} 
            #endregion
        }

        private void OnCreateSqltClicked(object sender, EventArgs e)
        {
            // Patho to folder "sf10"
            string inputDir = txtSymcmsPath.Text;
            SymcmsCreatorBase creator = new SymcmsCreatorBase();
            string[] files = creator.GetSqlFilesList(inputDir);
            string outputFile = "d:\\temp\\!complete.sql";

            creator.CreateSqlFile(new List<string>(files), outputFile);
            Debug("SQL file is saved as {0}...", outputFile);
            Log4cs.Log("Cleaning SQL file...");
            string sql = creator.GetCleanSql(outputFile);
            File.WriteAllText(outputFile + ".clean.sql", sql);
            //for(int i = 0; i < files.Length; i++)
            //{
            //    Debug(files[i]);
            //}
        }

        private void OnSymfonyInstallDirectoryClicked(object sender, EventArgs e)
        {
            try
            {
                FolderBrowserDialog dlg = new FolderBrowserDialog();
                if(txtSymfonyInstallDir.Text.Length > 0)
                {
                    dlg.SelectedPath = txtSymfonyInstallDir.Text;
                }
                if(dlg.ShowDialog() == DialogResult.OK)
                {
                    txtSymfonyInstallDir.Text = dlg.SelectedPath;
                }
            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error setting symfony installation directory!");
                Log4cs.Log(Importance.Debug, ex.ToString());
                
            }
        }

    }  // END CLASS Form1

}
